      
import hmac
import time
import requests
from datetime import datetime
import hashlib
import uuid
import base64
import os
from urllib.parse import urlparse



class Kling:
    def __init__(self, ak='', sk='', interval=5):
        """
        :param ak
        :param sk
        :param interval 轮询间隔
        """
        self.ak = ak
        self.sk = sk
        self.time_stamp = int(datetime.now().timestamp() * 1000)  # 毫秒级时间戳
        self.signature_nonce = uuid.uuid1()  # 随机字符串
        self.signature_kling_text2video_generate = self._hash_kling_text2video_generate_sk(self.sk, self.time_stamp, self.signature_nonce)
        self.signature_kling_img2video_generate = self._hash_kling_img2video_generate_sk(self.sk, self.time_stamp, self.signature_nonce)
        self.signature_kling_multiimg2video_generate = self._hash_kling_multiimg2video_generate_sk(self.sk, self.time_stamp, self.signature_nonce)
        self.signature_generate_status = self._hash_generate_status_sk(self.sk, self.time_stamp, self.signature_nonce)
        self.interval = interval
        self.headers = {'Content-Type': 'application/json'}        

        self.generate_kling_text2video = self.kling_text2video_generate(self.ak, self.signature_kling_text2video_generate, self.time_stamp,
                                                  self.signature_nonce)
        self.get_generate_status = self.get_generate_status_url(self.ak, self.signature_generate_status, self.time_stamp,
                                                               self.signature_nonce)
        self.generate_kling_img2video = self.kling_img2video_generate(self.ak, self.signature_kling_img2video_generate, self.time_stamp,
                                                  self.signature_nonce)
        self.generate_kling_multiimg2video = self.kling_multiimg2video_generate(self.ak, self.signature_kling_multiimg2video_generate, self.time_stamp,
                                                  self.signature_nonce)
    def hmac_sha1(self, key, code):
        hmac_code = hmac.new(key.encode(), code.encode(), hashlib.sha1)
        return hmac_code.digest()

    def _hash_kling_text2video_generate_sk(self, key, s_time, ro):
        """加密sk"""
        data = "/api/generate/video/kling/text2video" + "&" + str(s_time) + "&" + str(ro)
        s = base64.urlsafe_b64encode(self.hmac_sha1(key, data)).rstrip(b'=').decode()
        return s
    def _hash_kling_img2video_generate_sk(self, key, s_time, ro):
        """加密sk"""
        data = "/api/generate/video/kling/img2video" + "&" + str(s_time) + "&" + str(ro)
        s = base64.urlsafe_b64encode(self.hmac_sha1(key, data)).rstrip(b'=').decode()
        return s
    def _hash_kling_multiimg2video_generate_sk(self, key, s_time, ro):
        """加密sk"""
        data = "/api/generate/video/kling/multiImg2video" + "&" + str(s_time) + "&" + str(ro)
        s = base64.urlsafe_b64encode(self.hmac_sha1(key, data)).rstrip(b'=').decode()
        return s
    def _hash_generate_status_sk(self, key, s_time, ro):
        """加密sk"""
        data = "/api/generate/status" + "&" + str(s_time) + "&" + str(ro)
        s = base64.urlsafe_b64encode(self.hmac_sha1(key, data)).rstrip(b'=').decode()
        return s
    
    def kling_text2video_generate(self, ak, signature, time_stamp, signature_nonce):
        url = f"https://openapi.liblibai.cloud/api/generate/video/kling/text2video?AccessKey={ak}&Signature={signature}&Timestamp={time_stamp}&SignatureNonce={signature_nonce}"
        return url
    
    def kling_img2video_generate(self, ak, signature, time_stamp, signature_nonce):
        url = f"https://openapi.liblibai.cloud/api/generate/video/kling/img2video?AccessKey={ak}&Signature={signature}&Timestamp={time_stamp}&SignatureNonce={signature_nonce}"
        return url
    
    def kling_multiimg2video_generate(self, ak, signature, time_stamp, signature_nonce):
        url = f"https://openapi.liblibai.cloud/api/generate/video/kling/multiImg2video?AccessKey={ak}&Signature={signature}&Timestamp={time_stamp}&SignatureNonce={signature_nonce}"
        return url

    def get_generate_status_url(self, ak, signature, time_stamp, signature_nonce):
        url = f"https://openapi.liblibai.cloud/api/generate/status?AccessKey={ak}&Signature={signature}&Timestamp={time_stamp}&SignatureNonce={signature_nonce}"
        return url


    def kling_text2video(self):
        base_json = {
            "templateUuid":"61cd8b60d340404394f2a545eeaf197a",
            "generateParams":{
                "model": "kling-v2-1-master",
                "prompt":"一个摇滚乐队的演出现场，主唱拿着麦克风在台上唱歌，吉他手在卖力弹吉他，贝斯手弹贝斯，鼓手在摇头晃脑的在敲鼓，键盘手在弹钢琴。",
                "promptMagic":1,
                "aspectRatio":"16:9",
                "duration":"5"
            }
        }
        return self.run(base_json, self.generate_kling_text2video)

    def kling_img2video_startframe(self, prompt = '', imgurl = ''):
        base_json = {
            "templateUuid":"180f33c6748041b48593030156d2a71d",
            "generateParams":{
                # "model": "kling-v2-1",
                "model": "kling-v1-6",
                "prompt":prompt,
                "promptMagic":1,
                "mode":"pro",
                # "startFrame":"https://liblibai-online.liblib.cloud/img/9b0e9abdefc9f3ab198b6677feb42c89/ca89839d4ba8c5eba0521ea003106c99e6df9286c53bfae12c2e7852c634fdf4.png",
                # "startFrame":"https://test-yitu.oss-cn-shanghai.aliyuncs.com/workshop/335//44caebb424df5f9e22cd43d74a091e59.jpg",
                "startFrame":imgurl,
                "duration":"5"
            }
        }
        return self.run(base_json, self.generate_kling_img2video)

    def kling_img2video_startEndframe(self):
        base_json = {
            "templateUuid":"180f33c6748041b48593030156d2a71d",
            "generateParams":{
                "model": "kling-v1-6",
                "prompt":"镜头前拿着宣传海报的手放下",
                "promptMagic":1,
                "mode":"pro",
                "startFrame":"https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/097297f639b8a2850be8187c2a8d9465dc1afabfb813b76f6c188effd42a34c4.png",
                "endFrame":"https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/da0e158ebeb23a52d832b16f45c0ac43d7c60f07e36bdc0a438602c4a251cfab.png",
                "duration":"5"
            }
        }
        return self.run(base_json, self.generate_kling_img2video)

    def kling_multiimg2video(self):
        base_json = {
            "templateUuid":"ca01e798b4424587b0dfdb98b089da05",
            "generateParams":{
                "prompt":"一个卡通风格的老爷爷在咖啡馆里，端起咖啡杯喝咖啡",
                "promptMagic":1,
                "mode":"std",
                "referenceImages":[
                    "https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/b47511a385a1c3101624643dcf0748a9c669d5f8f97c5fa07fe0fb08b19af57d.jpeg",
                    "https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/5089fa6c3476e92255175a3246baa8db4c6c9e717d2aa1272cc4c74a0556c4c1.jpeg",
                    "https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/444c6469f498950feef471000338f3568c89b879402b3fc972efc089ab6f569f.jpeg"
                ],
                "aspectRatio":"16:9",
                "duration":"5"
            }
        }
        return self.run(base_json, self.generate_kling_multiimg2video)

    def run(self, data, url, timeout=300):
        """
        发送任务，直到返回videoUrl为止，失败抛出异常信息
        """
        start_time = time.time()  # 记录开始时间
        # 这里提交任务，校验是否提交成功，并且获取任务ID
        print(url)
        response = requests.post(url=url, headers=self.headers, json=data)
        response.raise_for_status()
        progress = response.json()
        print(progress)
        if progress['code'] == 0:
            # 如果获取到任务ID，执行等待生图
            while True:
                current_time = time.time()
                if (current_time - start_time) > timeout:
                    print(f"{timeout}s任务超时，已退出轮询。")
                    return None

                generate_uuid = progress["data"]['generateUuid']
                data = {"generateUuid": generate_uuid}
                response = requests.post(url=self.get_generate_status, headers=self.headers, json=data)
                response.raise_for_status()
                progress = response.json()
                print(progress)

                if progress['data'].get('generateStatus') == 6:
                    msg = progress['data'].get('generateMsg')
                    return f'任务失败, {msg}'

                if progress['data'].get('videos') and any(
                        video for video in progress['data']['videos'] if video is not None):
                    print("任务完成，获取到视频数据。")
                    return progress

                print(f"任务尚未完成，等待 {self.interval} 秒...")
                time.sleep(self.interval)                
        else:
            return f'任务失败,原因：code {progress["msg"]}'


def save_file_from_url(url, save_dir="downloads", filename=None):
    """从URL下载文件并保存到本地，支持自定义文件名"""
    if not url:
        print("URL为空，跳过下载")
        return None

    # 创建下载目录
    os.makedirs(save_dir, exist_ok=True)

    try:
        # 文件名可自定义，否则用原URL名
        if filename is None:
            parsed_url = urlparse(url)
            filename = os.path.basename(parsed_url.path)
        save_path = os.path.join(save_dir, filename)

        # 下载文件
        response = requests.get(url, stream=True)
        response.raise_for_status()  # 检查请求是否成功

        with open(save_path, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)

        print(f"文件已保存到: {save_path}")
        return save_path
    except Exception as e:
        print(f"下载失败: {str(e)}")
        return None

def main():
    ak='ZB4FqSMFuuGCg4pgRzMMOg'
    sk='S_uCEk4-CSJ63LSJeZiv_2WDxlJxLZ4x'
    '''
    {'code': 0, 'data': {'generateUuid': 'aaea8a7f4d024df6b3dc84880b40bbe9', 
    'generateStatus': 5, 'percentCompleted': 1.0, 
    'generateMsg': None, 'pointsCost': 455, 
    'accountBalance': 81781, 'images': [], 
    'videos': [{'videoUrl': 'https://liblibai-tmp-image.liblib.cloud/sd-images/872c6f380be8126d545c23b79c62c419f015034fecd414430308887cf3bba243.mp4', 
    'coverPath': 'https://liblibai-tmp-image.liblib.cloud/sd-images/164dfc2fdf184b420da0ce2b313421555b415899d531f0ece4c357b990c5d192.png', 
    'auditStatus': 3}], 'audios': []}, 'msg': ''}
    '''
    test = Kling(ak, sk)
    # print("可灵文生视频测试")
    # response = test.kling_text2video()
    # print("--"*100)
    # # 保存视频与封面
    # if response and isinstance(response, dict) and response.get("data", {}).get("videos"):
    #     for idx, video_info in enumerate(response["data"]["videos"]):
    #         video_url = video_info.get("videoUrl")
    #         cover_url = video_info.get("coverPath")
    #         if video_url:
    #             save_file_from_url(video_url, filename=f"text2video_{idx+1}.mp4")
    #         if cover_url:
    #             save_file_from_url(cover_url, filename=f"text2video_{idx+1}_cover.png")

    '''
    prompt固定为 360度展示这件衣服

    https://test-yitu.oss-cn-shanghai.aliyuncs.com/174/design/clothes/9cb5e205f378b2dc2fe3e4e758839e67.png
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/fdc352014e6dcf082935a2392f9c76d3.jpg
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/ad2c8f3052c3381f0324d96c652d9bfa.png
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/d920d4e31698d633caa9f026eaed7e1a.png
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/174/design/clothes/11af2bf4dc17e113302382a9730b9424.png
    '''

    print("可灵图生视频_首帧参考")
    response = test.kling_img2video_startframe()
    print("--"*100)
    if response and isinstance(response, dict) and response.get("data", {}).get("videos"):
        for idx, video_info in enumerate(response["data"]["videos"]):
            video_url = video_info.get("videoUrl")
            cover_url = video_info.get("coverPath")
            if video_url:
                save_file_from_url(video_url, filename=f"img2video_startframe_{idx+1}.mp4")
            if cover_url:
                save_file_from_url(cover_url, filename=f"img2video_startframe_{idx+1}_cover.png")

    # print("可灵图生视频_首帧参考+尾帧参考")
    # response = test.kling_img2video_startEndframe()
    # print("--"*100)
    # if response and isinstance(response, dict) and response.get("data", {}).get("videos"):
    #     for idx, video_info in enumerate(response["data"]["videos"]):
    #         video_url = video_info.get("videoUrl")
    #         cover_url = video_info.get("coverPath")
    #         if video_url:
    #             save_file_from_url(video_url, filename=f"img2video_startendframe_{idx+1}.mp4")
    #         if cover_url:
    #             save_file_from_url(cover_url, filename=f"img2video_startendframe_{idx+1}_cover.png")

    # print("可灵多图参考生成视频")
    # response = test.kling_multiimg2video()
    # print("--"*100)
    # if response and isinstance(response, dict) and response.get("data", {}).get("videos"):
    #     for idx, video_info in enumerate(response["data"]["videos"]):
    #         video_url = video_info.get("videoUrl")
    #         cover_url = video_info.get("coverPath")
    #         if video_url:
    #             save_file_from_url(video_url, filename=f"multiimg2video_{idx+1}.mp4")
    #         if cover_url:
    #             save_file_from_url(cover_url, filename=f"multiimg2video_{idx+1}_cover.png")


def test_startframe_multipy():
    ak='ZB4FqSMFuuGCg4pgRzMMOg'
    ak='icLHsdMyjegz5oNdb3hLAA'
    sk='S_uCEk4-CSJ63LSJeZiv_2WDxlJxLZ4x'
    sk='nBEsTIVe0xYrFvD2PZ9m9C_iOFFQz-PV'
    test = Kling(ak, sk)
    '''
    prompt固定为 360度展示这件衣服

    https://test-yitu.oss-cn-shanghai.aliyuncs.com/174/design/clothes/9cb5e205f378b2dc2fe3e4e758839e67.png
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/fdc352014e6dcf082935a2392f9c76d3.jpg
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/ad2c8f3052c3381f0324d96c652d9bfa.png
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/d920d4e31698d633caa9f026eaed7e1a.png
    https://test-yitu.oss-cn-shanghai.aliyuncs.com/174/design/clothes/11af2bf4dc17e113302382a9730b9424.png
    '''

    # 全部图片url
    img_urls = [
        "https://test-yitu.oss-cn-shanghai.aliyuncs.com/174/design/clothes/9cb5e205f378b2dc2fe3e4e758839e67.png",
        # "https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/fdc352014e6dcf082935a2392f9c76d3.jpg",
        # "https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/ad2c8f3052c3381f0324d96c652d9bfa.png",
        # "https://test-yitu.oss-cn-shanghai.aliyuncs.com/175/design/clothes/d920d4e31698d633caa9f026eaed7e1a.png",
        # "https://test-yitu.oss-cn-shanghai.aliyuncs.com/174/design/clothes/11af2bf4dc17e113302382a9730b9424.png"
    ]

    prompt = "360度展示这件服装,着重展示后背"

    print("可灵图生视频_首帧参考[多图循环]")
    for idx, url in enumerate(img_urls):
        response = test.kling_img2video_startframe(prompt=prompt, imgurl=url)
        print(f"--{idx+1}--", url)
        if response and isinstance(response, dict) and response.get("data", {}).get("videos"):
            for v_idx, video_info in enumerate(response["data"]["videos"]):
                video_url = video_info.get("videoUrl")
                cover_url = video_info.get("coverPath")
                if video_url:
                    save_file_from_url(video_url, filename=f"img2video_startframe_{idx+1}.mp4")
                if cover_url:
                    save_file_from_url(cover_url, filename=f"img2video_startframe_{idx+1}_cover.png")

def test():
    data = {'code': 0, 'data': {'generateUuid': 'aaea8a7f4d024df6b3dc84880b40bbe9', 
        'generateStatus': 5, 'percentCompleted': 1.0, 
        'generateMsg': None, 'pointsCost': 455, 
        'accountBalance': 81781, 'images': [], 
        'videos': [{'videoUrl': 'https://liblibai-tmp-image.liblib.cloud/sd-images/872c6f380be8126d545c23b79c62c419f015034fecd414430308887cf3bba243.mp4', 
        'coverPath': 'https://liblibai-tmp-image.liblib.cloud/sd-images/164dfc2fdf184b420da0ce2b313421555b415899d531f0ece4c357b990c5d192.png', 
        'auditStatus': 3}], 'audios': []}, 'msg': ''}
    # 下载视频和封面
    video_url = data['data']['videos'][0]['videoUrl']
    cover_url = data['data']['videos'][0]['coverPath']

    save_file_from_url(video_url)
    save_file_from_url(cover_url)


if __name__ == '__main__':
    # main()
    test_startframe_multipy()


    